package com.unpay.skb.requests;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.unpay.skb.responses.UnpayResponse;
import com.unpay.skb.util.Config;
import com.unpay.skb.util.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;

/**
 * 中付请求基类
 */
public abstract class UnpayRequest<T extends UnpayResponse> {
    private static final Logger logger = LoggerFactory.getLogger(UnpayRequest.class);

    private String merId = Config.instance().getMerId();//商户号
    private String signature;//签名

    public String getMerId() {
        return merId;
    }

    public String getSignature() {
        return signature;
    }

    public void setSignature(String signature) {
        this.signature = signature;
    }

    public T exec() {
        String result;
        try {
            Map<String, String> returnMap = requestData();
            String resJson = JSONObject.toJSONString(returnMap);
            HttpClient client = new HttpClient(this.getClass().getName()
                    .equals("com.unpay.skb.requests.ReplacePayRequest")?Config.instance().getPayurl():Config.instance().getUrl(),
                    300000, 300000);
            client.send(resJson, "UTF-8");
            result = client.getResult();
        } catch (Exception e) {
            logger.error(JSON.toJSONString(this)+":"+e.getMessage());
            result = "{\"status\":\"1\",\"msg\":\"" + e.getMessage() + "\"}";
        }
        T res=JSONObject.toJavaObject(JSONObject.parseObject(result), responseClass());
        if (!res.isSuccess()) {
            logger.error(JSON.toJSONString(this)+":"+result);
        }
        return res;
    }

    protected Map<String, String> requestData() throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        Class<? extends Object> clazz = this.getClass();
        Map<String, String> returnMap = new TreeMap<>();
        BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (int i = 0; i < propertyDescriptors.length; i++) {
            PropertyDescriptor descriptor = propertyDescriptors[i];
            String propertyName = descriptor.getName();
            if (!propertyName.equals("class")) {
                Method readMethod = descriptor.getReadMethod();
                Object vaule = readMethod.invoke(this, new Object[0]);
                returnMap.put(propertyName, vaule == null ? "" : vaule.toString());
            }
        }
        String str = joinMapValue(returnMap, '&');
        String signature = sha(str + sha(Config.instance().getKey()));
        returnMap.put("signature", signature);
        return returnMap;
    }

    abstract Class<T> responseClass();

    public String joinMapValue(Map<String, String> map, char connector) {
        TreeMap<String, String> tm = new TreeMap<>(map);
        StringBuffer b = new StringBuffer();
        for (Map.Entry<String, String> entry : tm.entrySet()) {
            if ("signature".equals(entry.getKey())) {
                continue;
            }
            b.append(entry.getKey());
            b.append('=');
            if (entry.getValue() != null) {
                b.append(entry.getValue());
            }
            b.append(connector);
        }
        return b.toString();
    }


    public String sha(String str) {
        if (str == null) {
            return null;
        }
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.reset();
            messageDigest.update(str.getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        byte[] byteArray = messageDigest.digest();
        StringBuffer strBuff = new StringBuffer();
        for (int i = 0; i < byteArray.length; i++) {
            if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
                strBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
            else
                strBuff.append(Integer.toHexString(0xFF & byteArray[i]));
        }
        return strBuff.toString();
    }
}
